package com.tcp2.util;

import com.google.common.util.concurrent.SettableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeoutException;

/**
 * Author: 程行荣
 * Time: 2014-11-24 19:11
 * Copyright (C) 2014 Xiamen Yaxon Networks CO.,LTD.
 */

/**
 * 推迟结果
 */
public class DeferredResultUtil {
    private static Logger logger = LoggerFactory.getLogger(DeferredResultUtil.class);

    private static DeferredResultUtil instance;

    public static final int DEFAULT_TIMEOUT = 30000;
    private ConcurrentMap<String, DeferredResult> deferredResults = new ConcurrentHashMap<>();

    private Thread loopThread = new Thread("DeferredResultUtil-loop") {
        @Override
        public void run() {
            int ticks = 0;
            while (true) {
                ++ticks;
                long tm = System.currentTimeMillis();
                try {
                    if (ticks % 10 == 0) {
                        for (Map.Entry<String, DeferredResult> entry : deferredResults.entrySet()) {
                            if (entry.getValue().getTimestamp() < tm - entry.getValue().getTimeout()) {
                                DeferredResult dr = deferredResults.remove(entry.getKey());
                                if (dr != null) {
                                    dr.getFuture().setException(new TimeoutException());
                                }
                            }
                        }
                    }
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                } catch (Exception e) {
                    logger.warn("线程异常", e);
                }
            }
            logger.info("线程({})已停止", getName());
        }
    };

    public DeferredResultUtil() {
        loopThread.setDaemon(true);
        loopThread.start();
    }

    public static DeferredResultUtil getInstance() {
        if (instance == null) {
            instance = new DeferredResultUtil();
        }
        return instance;
    }

    public void putFuture(String fno, SettableFuture future) {
        putFuture(fno, future, DEFAULT_TIMEOUT);
    }

    public boolean putFuture(String fno, SettableFuture future, long timeout) {
        if (deferredResults.putIfAbsent(fno,
                new DeferredResult(timeout, System.currentTimeMillis(),future)) == null) {
            return true;
        } else {
            return false;
        }
    }

    public void setResult(String no, Object res) {
        DeferredResult dr = deferredResults.remove(no);
        if (dr != null) {
            dr.getFuture().set(res);
        }
    }

    public void setExcepion(String no, Throwable t) {
        DeferredResult dr = deferredResults.remove(no);
        if (dr != null) {
            dr.getFuture().setException(t);
        }
    }

    public class DeferredResult {
        private long timeout;
        private long timestamp;
        private SettableFuture future;

        public DeferredResult(long timeout, long timestamp, SettableFuture future) {
            this.timeout = timeout;
            this.timestamp = timestamp;
            this.future = future;
        }

        public long getTimeout() {
            return timeout;
        }

        public SettableFuture getFuture() {
            return future;
        }

        public long getTimestamp() {
            return timestamp;
        }
    }
}
